home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / XML / Parser.php < prev    next >
Encoding:
PHP Script  |  2005-09-26  |  18.5 KB  |  685 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Stig Bakken <ssb@fast.no>                                    |
  17. // |         Tomas V.V.Cox <cox@idecnet.com>                              |
  18. // |         Stephan Schmidt <schst@php-tools.net>                        |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: Parser.php,v 1.26 2005/09/23 11:51:10 schst Exp $
  22.  
  23. /**
  24.  * XML Parser class.
  25.  *
  26.  * This is an XML parser based on PHP's "xml" extension,
  27.  * based on the bundled expat library.
  28.  *
  29.  * @category XML
  30.  * @package XML_Parser
  31.  * @author  Stig Bakken <ssb@fast.no>
  32.  * @author  Tomas V.V.Cox <cox@idecnet.com>
  33.  * @author  Stephan Schmidt <schst@php-tools.net>
  34.  */
  35.  
  36. /**
  37.  * uses PEAR's error handling
  38.  */
  39. require_once 'PEAR.php';
  40.  
  41. /**
  42.  * resource could not be created
  43.  */
  44. define('XML_PARSER_ERROR_NO_RESOURCE', 200);
  45.  
  46. /**
  47.  * unsupported mode
  48.  */
  49. define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
  50.  
  51. /**
  52.  * invalid encoding was given
  53.  */
  54. define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
  55.  
  56. /**
  57.  * specified file could not be read
  58.  */
  59. define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
  60.  
  61. /**
  62.  * invalid input
  63.  */
  64. define('XML_PARSER_ERROR_INVALID_INPUT', 204);
  65.  
  66. /**
  67.  * remote file cannot be retrieved in safe mode
  68.  */
  69. define('XML_PARSER_ERROR_REMOTE', 205);
  70.  
  71. /**
  72.  * XML Parser class.
  73.  *
  74.  * This is an XML parser based on PHP's "xml" extension,
  75.  * based on the bundled expat library.
  76.  *
  77.  * Notes:
  78.  * - It requires PHP 4.0.4pl1 or greater
  79.  * - From revision 1.17, the function names used by the 'func' mode
  80.  *   are in the format "xmltag_$elem", for example: use "xmltag_name"
  81.  *   to handle the <name></name> tags of your xml file.
  82.  *
  83.  * @category XML
  84.  * @package XML_Parser
  85.  * @author  Stig Bakken <ssb@fast.no>
  86.  * @author  Tomas V.V.Cox <cox@idecnet.com>
  87.  * @author  Stephan Schmidt <schst@php-tools.net>
  88.  * @todo    create XML_Parser_Namespace to parse documents with namespaces
  89.  * @todo    create XML_Parser_Pull
  90.  * @todo    Tests that need to be made:
  91.  *          - mixing character encodings
  92.  *          - a test using all expat handlers
  93.  *          - options (folding, output charset)
  94.  *          - different parsing modes
  95.  */
  96. class XML_Parser extends PEAR
  97. {
  98.     // {{{ properties
  99.  
  100.    /**
  101.      * XML parser handle
  102.      *
  103.      * @var  resource
  104.      * @see  xml_parser_create()
  105.      */
  106.     var $parser;
  107.  
  108.     /**
  109.      * File handle if parsing from a file
  110.      *
  111.      * @var  resource
  112.      */
  113.     var $fp;
  114.  
  115.     /**
  116.      * Whether to do case folding
  117.      *
  118.      * If set to true, all tag and attribute names will
  119.      * be converted to UPPER CASE.
  120.      *
  121.      * @var  boolean
  122.      */
  123.     var $folding = true;
  124.  
  125.     /**
  126.      * Mode of operation, one of "event" or "func"
  127.      *
  128.      * @var  string
  129.      */
  130.     var $mode;
  131.  
  132.     /**
  133.      * Mapping from expat handler function to class method.
  134.      *
  135.      * @var  array
  136.      */
  137.     var $handler = array(
  138.         'character_data_handler'            => 'cdataHandler',
  139.         'default_handler'                   => 'defaultHandler',
  140.         'processing_instruction_handler'    => 'piHandler',
  141.         'unparsed_entity_decl_handler'      => 'unparsedHandler',
  142.         'notation_decl_handler'             => 'notationHandler',
  143.         'external_entity_ref_handler'       => 'entityrefHandler'
  144.     );
  145.  
  146.     /**
  147.      * source encoding
  148.      *
  149.      * @var string
  150.      */
  151.     var $srcenc;
  152.  
  153.     /**
  154.      * target encoding
  155.      *
  156.      * @var string
  157.      */
  158.     var $tgtenc;
  159.  
  160.     /**
  161.      * handler object
  162.      *
  163.      * @var object
  164.      */
  165.     var $_handlerObj;
  166.  
  167.     // }}}
  168.     // {{{ constructor
  169.  
  170.     /**
  171.      * Creates an XML parser.
  172.      *
  173.      * This is needed for PHP4 compatibility, it will
  174.      * call the constructor, when a new instance is created.
  175.      *
  176.      * @param string $srcenc source charset encoding, use NULL (default) to use
  177.      *                       whatever the document specifies
  178.      * @param string $mode   how this parser object should work, "event" for
  179.      *                       startelement/endelement-type events, "func"
  180.      *                       to have it call functions named after elements
  181.      * @param string $tgenc  a valid target encoding
  182.      */
  183.     function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
  184.     {
  185.         XML_Parser::__construct($srcenc, $mode, $tgtenc);
  186.     }
  187.     // }}}
  188.  
  189.     /**
  190.      * PHP5 constructor
  191.      *
  192.      * @param string $srcenc source charset encoding, use NULL (default) to use
  193.      *                       whatever the document specifies
  194.      * @param string $mode   how this parser object should work, "event" for
  195.      *                       startelement/endelement-type events, "func"
  196.      *                       to have it call functions named after elements
  197.      * @param string $tgenc  a valid target encoding
  198.      */
  199.     function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
  200.     {
  201.         $this->PEAR('XML_Parser_Error');
  202.  
  203.         $this->mode   = $mode;
  204.         $this->srcenc = $srcenc;
  205.         $this->tgtenc = $tgtenc;
  206.     }
  207.     // }}}
  208.  
  209.     /**
  210.      * Sets the mode of the parser.
  211.      *
  212.      * Possible modes are:
  213.      * - func
  214.      * - event
  215.      *
  216.      * You can set the mode using the second parameter
  217.      * in the constructor.
  218.      *
  219.      * This method is only needed, when switching to a new
  220.      * mode at a later point.
  221.      *
  222.      * @access  public
  223.      * @param   string          mode, either 'func' or 'event'
  224.      * @return  boolean|object  true on success, PEAR_Error otherwise   
  225.      */
  226.     function setMode($mode)
  227.     {
  228.         if ($mode != 'func' && $mode != 'event') {
  229.             $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
  230.         }
  231.  
  232.         $this->mode = $mode;
  233.         return true;
  234.     }
  235.  
  236.     /**
  237.      * Sets the object, that will handle the XML events
  238.      *
  239.      * This allows you to create a handler object independent of the
  240.      * parser object that you are using and easily switch the underlying
  241.      * parser.
  242.      *
  243.      * If no object will be set, XML_Parser assumes that you
  244.      * extend this class and handle the events in $this.
  245.      *
  246.      * @access  public
  247.      * @param   object      object to handle the events
  248.      * @return  boolean     will always return true
  249.      * @since   v1.2.0beta3
  250.      */
  251.     function setHandlerObj(&$obj)
  252.     {
  253.         $this->_handlerObj = &$obj;
  254.         return true;
  255.     }
  256.  
  257.     /**
  258.      * Init the element handlers
  259.      *
  260.      * @access  private
  261.      */
  262.     function _initHandlers()
  263.     {
  264.         if (!is_resource($this->parser)) {
  265.             return false;
  266.         }
  267.  
  268.         if (!is_object($this->_handlerObj)) {
  269.             $this->_handlerObj = &$this;
  270.         }
  271.         switch ($this->mode) {
  272.  
  273.             case 'func':
  274.                 xml_set_object($this->parser, $this->_handlerObj);
  275.                 xml_set_element_handler($this->parser, array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
  276.                 break;
  277.  
  278.             case 'event':
  279.                 xml_set_object($this->parser, $this->_handlerObj);
  280.                 xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
  281.                 break;
  282.             default:
  283.                 return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
  284.                 break;
  285.         }
  286.  
  287.  
  288.         /**
  289.          * set additional handlers for character data, entities, etc.
  290.          */
  291.         foreach ($this->handler as $xml_func => $method) {
  292.             if (method_exists($this->_handlerObj, $method)) {
  293.                 $xml_func = 'xml_set_' . $xml_func;
  294.                 $xml_func($this->parser, $method);
  295.             }
  296.         }
  297.     }
  298.  
  299.     // {{{ _create()
  300.  
  301.     /**
  302.      * create the XML parser resource
  303.      *
  304.      * Has been moved from the constructor to avoid
  305.      * problems with object references.
  306.      *
  307.      * Furthermore it allows us returning an error
  308.      * if something fails.
  309.      *
  310.      * @access   private
  311.      * @return   boolean|object     true on success, PEAR_Error otherwise
  312.      *
  313.      * @see xml_parser_create
  314.      */
  315.     function _create()
  316.     {
  317.         if ($this->srcenc === null) {
  318.             $xp = @xml_parser_create();
  319.         } else {
  320.             $xp = @xml_parser_create($this->srcenc);
  321.         }
  322.         if (is_resource($xp)) {
  323.             if ($this->tgtenc !== null) {
  324.                 if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
  325.                                             $this->tgtenc)) {
  326.                     return $this->raiseError('invalid target encoding', XML_PARSER_ERROR_INVALID_ENCODING);
  327.                 }
  328.             }
  329.             $this->parser = $xp;
  330.             $result = $this->_initHandlers($this->mode);
  331.             if ($this->isError($result)) {
  332.                 return $result;
  333.             }
  334.             xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
  335.  
  336.             return true;
  337.         }
  338.         return $this->raiseError('Unable to create XML parser resource.', XML_PARSER_ERROR_NO_RESOURCE);
  339.     }
  340.  
  341.     // }}}
  342.     // {{{ reset()
  343.  
  344.     /**
  345.      * Reset the parser.
  346.      *
  347.      * This allows you to use one parser instance
  348.      * to parse multiple XML documents.
  349.      *
  350.      * @access   public
  351.      * @return   boolean|object     true on success, PEAR_Error otherwise
  352.      */
  353.     function reset()
  354.     {
  355.         $result = $this->_create();
  356.         if ($this->isError( $result )) {
  357.             return $result;
  358.         }
  359.         return true;
  360.     }
  361.  
  362.     // }}}
  363.     // {{{ setInputFile()
  364.  
  365.     /**
  366.      * Sets the input xml file to be parsed
  367.      *
  368.      * @param    string      Filename (full path)
  369.      * @return   resource    fopen handle of the given file
  370.      * @throws   XML_Parser_Error
  371.      * @see      setInput(), setInputString(), parse()
  372.      * @access   public
  373.      */
  374.     function setInputFile($file)
  375.     {
  376.         /**
  377.          * check, if file is a remote file
  378.          */
  379.         if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
  380.             if (!ini_get('allow_url_fopen')) {
  381.                 return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE);
  382.             }
  383.         }
  384.         
  385.         $fp = @fopen($file, 'rb');
  386.         if (is_resource($fp)) {
  387.             $this->fp = $fp;
  388.             return $fp;
  389.         }
  390.         return $this->raiseError('File could not be opened.', XML_PARSER_ERROR_FILE_NOT_READABLE);
  391.     }
  392.  
  393.     // }}}
  394.     // {{{ setInputString()
  395.     
  396.     /**
  397.      * XML_Parser::setInputString()
  398.      * 
  399.      * Sets the xml input from a string
  400.      * 
  401.      * @param string $data a string containing the XML document
  402.      * @return null
  403.      **/
  404.     function setInputString($data)
  405.     {
  406.         $this->fp = $data;
  407.         return null;
  408.     }
  409.     
  410.     // }}}
  411.     // {{{ setInput()
  412.  
  413.     /**
  414.      * Sets the file handle to use with parse().
  415.      *
  416.      * You should use setInputFile() or setInputString() if you
  417.      * pass a string 
  418.      *
  419.      * @param    mixed  $fp  Can be either a resource returned from fopen(),
  420.      *                       a URL, a local filename or a string.
  421.      * @access   public
  422.      * @see      parse()
  423.      * @uses     setInputString(), setInputFile()
  424.      */
  425.     function setInput($fp)
  426.     {
  427.         if (is_resource($fp)) {
  428.             $this->fp = $fp;
  429.             return true;
  430.         }
  431.         // see if it's an absolute URL (has a scheme at the beginning)
  432.         elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
  433.             return $this->setInputFile($fp);
  434.         }
  435.         // see if it's a local file
  436.         elseif (file_exists($fp)) {
  437.             return $this->setInputFile($fp);
  438.         }
  439.         // it must be a string
  440.         else {
  441.             $this->fp = $fp;
  442.             return true;
  443.         }
  444.  
  445.         return $this->raiseError('Illegal input format', XML_PARSER_ERROR_INVALID_INPUT);
  446.     }
  447.  
  448.     // }}}
  449.     // {{{ parse()
  450.  
  451.     /**
  452.      * Central parsing function.
  453.      *
  454.      * @return   true|object PEAR error     returns true on success, or a PEAR_Error otherwise
  455.      * @access   public
  456.      */
  457.     function parse()
  458.     {
  459.         /**
  460.          * reset the parser
  461.          */
  462.         $result = $this->reset();
  463.         if ($this->isError($result)) {
  464.             return $result;
  465.         }
  466.         // if $this->fp was fopened previously
  467.         if (is_resource($this->fp)) {
  468.         
  469.             while ($data = fread($this->fp, 4096)) {
  470.                 if (!$this->_parseString($data, feof($this->fp))) {
  471.                     $error = &$this->raiseError();
  472.                     $this->free();
  473.                     return $error;
  474.                 }
  475.             }
  476.         // otherwise, $this->fp must be a string
  477.         } else {
  478.             if (!$this->_parseString($this->fp, true)) {
  479.                 $error = &$this->raiseError();
  480.                 $this->free();
  481.                 return $error;
  482.             }
  483.         }
  484.         $this->free();
  485.  
  486.         return true;
  487.     }
  488.  
  489.     /**
  490.      * XML_Parser::_parseString()
  491.      * 
  492.      * @param string $data
  493.      * @param boolean $eof
  494.      * @return bool
  495.      * @access private
  496.      * @see parseString()
  497.      **/
  498.     function _parseString($data, $eof = false)
  499.     {
  500.         return xml_parse($this->parser, $data, $eof);
  501.     }
  502.     
  503.     // }}}
  504.     // {{{ parseString()
  505.  
  506.     /**
  507.      * XML_Parser::parseString()
  508.      * 
  509.      * Parses a string.
  510.      *
  511.      * @param    string  $data XML data
  512.      * @param    boolean $eof  If set and TRUE, data is the last piece of data sent in this parser
  513.      * @throws   XML_Parser_Error
  514.      * @return   Pear Error|true   true on success or a PEAR Error
  515.      * @see      _parseString()
  516.      */
  517.     function parseString($data, $eof = false)
  518.     {
  519.         if (!isset($this->parser) || !is_resource($this->parser)) {
  520.             $this->reset();
  521.         }
  522.         
  523.         if (!$this->_parseString($data, $eof)) {
  524.            $error = &$this->raiseError();
  525.            $this->free();
  526.            return $error;
  527.         }
  528.  
  529.         if ($eof === true) {
  530.             $this->free();
  531.         }
  532.         return true;
  533.     }
  534.     
  535.     /**
  536.      * XML_Parser::free()
  537.      * 
  538.      * Free the internal resources associated with the parser
  539.      * 
  540.      * @return null
  541.      **/
  542.     function free()
  543.     {
  544.         if (isset($this->parser) && is_resource($this->parser)) {
  545.             xml_parser_free($this->parser);
  546.             unset( $this->parser );
  547.         }
  548.         if (isset($this->fp) && is_resource($this->fp)) {
  549.             fclose($this->fp);
  550.         }
  551.         unset($this->fp);
  552.         return null;
  553.     }
  554.     
  555.     /**
  556.      * XML_Parser::raiseError()
  557.      * 
  558.      * Throws a XML_Parser_Error
  559.      * 
  560.      * @param string  $msg   the error message
  561.      * @param integer $ecode the error message code
  562.      * @return XML_Parser_Error 
  563.      **/
  564.     function raiseError($msg = null, $ecode = 0)
  565.     {
  566.         $msg = !is_null($msg) ? $msg : $this->parser;
  567.         $err = &new XML_Parser_Error($msg, $ecode);
  568.         return parent::raiseError($err);
  569.     }
  570.     
  571.     // }}}
  572.     // {{{ funcStartHandler()
  573.  
  574.     function funcStartHandler($xp, $elem, $attribs)
  575.     {
  576.         $func = 'xmltag_' . $elem;
  577.         if (strchr($func, '.')) {
  578.             $func = str_replace('.', '_', $func);
  579.         }
  580.         if (method_exists($this->_handlerObj, $func)) {
  581.             call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
  582.         } elseif (method_exists($this->_handlerObj, 'xmltag')) {
  583.             call_user_func(array(&$this->_handlerObj, 'xmltag'), $xp, $elem, $attribs);
  584.         }
  585.     }
  586.  
  587.     // }}}
  588.     // {{{ funcEndHandler()
  589.  
  590.     function funcEndHandler($xp, $elem)
  591.     {
  592.         $func = 'xmltag_' . $elem . '_';
  593.         if (strchr($func, '.')) {
  594.             $func = str_replace('.', '_', $func);
  595.         }
  596.         if (method_exists($this->_handlerObj, $func)) {
  597.             call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
  598.         } elseif (method_exists($this->_handlerObj, 'xmltag_')) {
  599.             call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
  600.         }
  601.     }
  602.  
  603.     // }}}
  604.     // {{{ startHandler()
  605.  
  606.     /**
  607.      *
  608.      * @abstract
  609.      */
  610.     function startHandler($xp, $elem, &$attribs)
  611.     {
  612.         return NULL;
  613.     }
  614.  
  615.     // }}}
  616.     // {{{ endHandler()
  617.  
  618.     /**
  619.      *
  620.      * @abstract
  621.      */
  622.     function endHandler($xp, $elem)
  623.     {
  624.         return NULL;
  625.     }
  626.  
  627.  
  628.     // }}}me
  629. }
  630.  
  631. /**
  632.  * error class, replaces PEAR_Error
  633.  *
  634.  * An instance of this class will be returned
  635.  * if an error occurs inside XML_Parser.
  636.  *
  637.  * There are three advantages over using the standard PEAR_Error:
  638.  * - All messages will be prefixed
  639.  * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
  640.  * - messages can be generated from the xml_parser resource
  641.  *
  642.  * @package XML_Parser
  643.  * @access  public
  644.  * @see     PEAR_Error
  645.  */
  646. class XML_Parser_Error extends PEAR_Error
  647. {
  648.     // {{{ properties
  649.  
  650.    /**
  651.     * prefix for all messages
  652.     *
  653.     * @var      string
  654.     */    
  655.     var $error_message_prefix = 'XML_Parser: ';
  656.  
  657.     // }}}
  658.     // {{{ constructor()
  659.    /**
  660.     * construct a new error instance
  661.     *
  662.     * You may either pass a message or an xml_parser resource as first
  663.     * parameter. If a resource has been passed, the last error that
  664.     * happened will be retrieved and returned.
  665.     *
  666.     * @access   public
  667.     * @param    string|resource     message or parser resource
  668.     * @param    integer             error code
  669.     * @param    integer             error handling
  670.     * @param    integer             error level
  671.     */    
  672.     function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
  673.     {
  674.         if (is_resource($msgorparser)) {
  675.             $code = xml_get_error_code($msgorparser);
  676.             $msgorparser = sprintf('%s at XML input line %d:%d',
  677.                                    xml_error_string($code),
  678.                                    xml_get_current_line_number($msgorparser),
  679.                                    xml_get_current_column_number($msgorparser));
  680.         }
  681.         $this->PEAR_Error($msgorparser, $code, $mode, $level);
  682.     }
  683.     // }}}
  684. }
  685. ?>